#!/usr/bin/env python
# encoding: utf-8

"""
@version: python3.7
@Author  : huangyi
@contact:  hy.dazui@qq.com
@Time    : 2020/1/12 23:11
@Describe: add Describe here
"""
from enum import Enum

from ..scanner.token.token import TokenType, EndToken
from ..scanner.token.token_factory import TokenFactory


class State(Enum):
    NUMBER = 1  # 数字
    OP = 2  # 运算符
    LEFT_BRACKET = 3  # 左括号
    RIGHT_BRACKET = 4  # 右括号
    ZERO = 5
    POINT = 6  # 小数
    POINT_OP = 7  # 小数点
    FUNC = 8  # 方法
    COMMA = 9  # 逗号
    STRING = 10  # 字符串
    STRING_END = 11  # 字符串结束
    VAR = 12  # 变量
    VAR_END = 13  # 变量结束
    OP_KEY_WORD = 14  # 运算符关键字
    OP_KEY_WORD_END = 15  # 运算符关键字结束


class Scanner:

    def __init__(self):
        self.data = []

    def scan(self, expression: str) -> list:
        if not expression:
            return []
        last_state = None
        token_list = []
        for index, char in enumerate(expression):
            if last_state == State.STRING and char != "'":
                self.data.append(char)
            else:
                if char == ' ':
                    # 跳过空格
                    if last_state == State.FUNC:
                        # 方法后面只能紧跟着左括号
                        # 字符串后加空格，作为操作符识别，为了识别in, and, or等关键字
                        last_state = State.OP_KEY_WORD_END
                    elif last_state == State.OP_KEY_WORD:
                        last_state = State.OP_KEY_WORD_END
                    continue
                if char == '-':
                    if not last_state:
                        last_state = State.NUMBER
                    elif last_state == State.OP:
                        # 在操作符后面的一定负号或第一个符号
                        token_list.append(self.build_token(last_state))
                        last_state = State.NUMBER
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.VAR_END]:
                        # 当前状态是数值，则这时候负号是减号
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    else:
                        self.throw_error(expression, char, index)
                elif char == '(':
                    if not last_state:
                        last_state = State.LEFT_BRACKET
                    elif last_state in [State.OP, State.OP_KEY_WORD_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.LEFT_BRACKET
                    elif last_state == State.LEFT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.LEFT_BRACKET
                    elif last_state == State.FUNC:
                        token_list.append(self.build_token(last_state))
                        last_state = State.LEFT_BRACKET
                    else:
                        self.throw_error(expression, char, index)
                elif char == ')':
                    if last_state in [State.NUMBER, State.ZERO, State.POINT, State.LEFT_BRACKET,
                                      State.STRING_END, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.RIGHT_BRACKET
                    elif last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.RIGHT_BRACKET
                    else:
                        self.throw_error(expression, char, index)
                elif char == '+':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    else:
                        self.throw_error(expression, char, index)
                elif char == '*':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    else:
                        self.throw_error(expression, char, index)
                elif char == '&':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state == State.OP:
                        pass
                elif char == '|':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state == State.OP:
                        pass
                elif char == '/':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    else:
                        self.throw_error(expression, char, index)
                elif char == '>':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.STRING_END, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    else:
                        self.throw_error(expression, char, index)
                elif char == '<':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.STRING_END, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    else:
                        self.throw_error(expression, char, index)
                elif char == '=':
                    if last_state == State.RIGHT_BRACKET:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state in [State.NUMBER, State.ZERO, State.POINT, State.STRING_END, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP
                    elif last_state == State.OP:
                        pass
                    else:
                        self.throw_error(expression, char, index)
                elif char == '0':
                    if not last_state:
                        last_state = State.NUMBER
                    elif last_state in [State.OP, State.COMMA, State.OP_KEY_WORD_END, State.LEFT_BRACKET]:
                        # 最后一个状态是操作符，这里0只能是单个0
                        token_list.append(self.build_token(last_state))
                        last_state = State.ZERO
                    elif last_state in [State.NUMBER, State.POINT, State.FUNC, State.VAR]:
                        pass
                    elif last_state == State.POINT_OP:
                        last_state = State.POINT
                    else:
                        self.throw_error(expression, char, index)
                elif '1' <= char <= '9':
                    if not last_state:
                        last_state = State.NUMBER
                    elif last_state in [State.NUMBER, State.POINT, State.FUNC, State.VAR]:
                        pass
                    elif last_state in [State.POINT_OP]:
                        last_state = State.POINT
                    elif last_state in [State.OP, State.LEFT_BRACKET, State.COMMA, State.OP_KEY_WORD_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.NUMBER
                    elif last_state == State.STRING:
                        pass
                    else:
                        self.throw_error(expression, char, index)
                elif char == '.':
                    if last_state in [State.NUMBER, State.ZERO]:
                        last_state = State.POINT_OP
                    else:
                        self.throw_error(expression, char, index)
                elif 'a' <= char <= 'z' or 'A' <= char <= 'Z' or char in ['_']:
                    if not last_state:
                        last_state = State.FUNC
                    elif last_state in [State.FUNC, State.VAR, State.OP_KEY_WORD]:
                        pass
                    elif last_state in [State.OP, State.COMMA, State.OP_KEY_WORD_END, State.STRING_END,
                                        State.LEFT_BRACKET]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.FUNC
                    elif last_state in [State.NUMBER, State.POINT, State.ZERO, State.VAR_END, State.RIGHT_BRACKET]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.OP_KEY_WORD
                    else:
                        self.throw_error(expression, char, index)
                elif char == ',':
                    if not last_state:
                        self.throw_error(expression, char, index)
                    elif last_state in [State.NUMBER, State.POINT, State.ZERO, State.RIGHT_BRACKET,
                                        State.STRING_END, State.VAR_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.COMMA
                elif char == "'":
                    if not last_state:
                        last_state = State.STRING
                    elif last_state in [State.LEFT_BRACKET, State.COMMA, State.OP, State.OP_KEY_WORD_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.STRING
                    elif last_state == State.STRING:
                        last_state = State.STRING_END
                    else:
                        self.throw_error(expression, char, index)
                    continue
                elif char == "$":
                    if not last_state:
                        last_state = State.VAR
                    elif last_state in [State.LEFT_BRACKET, State.COMMA, State.OP, State.OP_KEY_WORD_END]:
                        token_list.append(self.build_token(last_state))
                        last_state = State.VAR
                    elif last_state == State.VAR:
                        last_state = State.VAR_END
                    else:
                        self.throw_error(expression, char, index)
                    continue
                else:
                    self.throw_error(expression, char, index)
                self.data.append(char)
        # 处理最后的数据
        if self.data:
            token_list.append(self.build_token(last_state))
        # 放入结束标志
        token_list.append(EndToken())
        return token_list

    def throw_error(self, expression, char, index):
        raise Exception("expression: {}, char '{}' at pos:{} is invalid!".format(expression, char, index))

    def build_token(self, state: State):
        token = None
        if state == State.RIGHT_BRACKET:
            token = TokenFactory.build_token(TokenType.RIGHT_BRACKET, "".join(self.data))
        elif state == State.LEFT_BRACKET:
            token = TokenFactory.build_token(TokenType.LEFT_BRACKET, "".join(self.data))
        elif state in [State.NUMBER, State.ZERO]:
            token = TokenFactory.build_token(TokenType.NUMBER, "".join(self.data))
        elif state == State.POINT:
            token = TokenFactory.build_token(TokenType.POINT, "".join(self.data))
        elif state in [State.OP, State.OP_KEY_WORD, State.OP_KEY_WORD_END]:
            token = TokenFactory.build_token(TokenType.OP, "".join(self.data))
        elif state == State.FUNC:
            token = TokenFactory.build_token(TokenType.FUNC, "".join(self.data))
        elif state == State.COMMA:
            token = TokenFactory.build_token(TokenType.COMMA, "".join(self.data))
        elif state in [State.STRING_END, State.STRING]:
            token = TokenFactory.build_token(TokenType.STRING, "".join(self.data))
        elif state in [State.VAR, State.VAR_END]:
            token = TokenFactory.build_token(TokenType.VAR, "".join(self.data))
        else:
            raise Exception("unsupported state {}".format(state))
        self.data = []
        return token
